承接上篇的介面設計,本文聚焦在「可運行的前端 CRUD 骨架」,以 CustomerManagement.vue
為中心,補齊資料模型、Store、API 契約、表單與刪除流程。
id
、code
、name
、phone
、email
、provider
、createdAt
-
// src/stores/customer.js
import { defineStore } from 'pinia';
export const useCustomerStore = defineStore('customer', {
state: () => ({
items: [],
loading: false,
error: null,
searchParams: { keyword: '' },
pagination: { currentPage: 1, pageSize: 10, totalItems: 0, totalPages: 0 },
}),
actions: {
async fetchCustomers() {
this.loading = true;
this.error = null;
try {
// TODO: 串接 API
// const res = await api.get('/api/customers', { params: { ...this.searchParams, ...this.pagination } });
// this.items = res.data.items;
// this.pagination = res.data.pagination;
} catch (err) {
this.error = err;
} finally {
this.loading = false;
}
},
setSearch(keyword) {
this.searchParams.keyword = keyword;
this.pagination.currentPage = 1;
this.fetchCustomers();
},
setPage(page) {
this.pagination.currentPage = page;
this.fetchCustomers();
},
},
});
GET /api/customers?keyword=xxx&page=1&pageSize=10
POST /api/customers
PUT /api/customers/:id
DELETE /api/customers/:id
{ items, pagination }
{ code, message, fields? }
<template>
<form @submit.prevent="onSubmit">
<label>姓名</label>
<input v-model.trim="form.name" required maxlength="50" />
<label>電話</label>
<input v-model.trim="form.phone" pattern="^[0-9\-]+$" required />
<label>Email</label>
<input v-model.trim="form.email" type="email" required />
<label>註冊方式</label>
<select v-model="form.provider">
<option value="email">Email</option>
<option value="line">LINE</option>
<option value="facebook">Facebook</option>
<option value="google">Google</option>
</select>
<div class="actions">
<BaseButton type="submit" variant="primary">儲存</BaseButton>
<BaseButton type="button" variant="secondary" @click="$emit('cancel')">取消</BaseButton>
</div>
</form>
</template>
<script setup>
import { reactive, watch } from 'vue';
const props = defineProps({
customer: Object, // 編輯時傳入的客戶資料
});
const emit = defineEmits(['submit', 'cancel']);
const form = reactive({
name: props.customer?.name || '',
phone: props.customer?.phone || '',
email: props.customer?.email || '',
provider: props.customer?.provider || 'email',
});
// 監聽 props 變化,更新表單
watch(
() => props.customer,
(newCustomer) => {
if (newCustomer) {
Object.assign(form, {
name: newCustomer.name || '',
phone: newCustomer.phone || '',
email: newCustomer.email || '',
provider: newCustomer.provider || 'email',
});
}
},
{ immediate: true },
);
function onSubmit() {
emit('submit', { ...form });
}
</script>
handleAddCustomer()
:// CustomerManagement.vue
function handleAddCustomer() {
showAddForm.value = true; // 控制表單顯示
editingCustomer.value = null; // 清空編輯資料
}
async function handleFormSubmit(formData) {
try {
// 新增客戶
const newCustomer = {
id: `C${Date.now()}`, // 臨時 ID
...formData,
createdAt: new Date().toISOString().split('T')[0],
};
customers.push(newCustomer);
// 關閉表單
showAddForm.value = false;
alert('客戶新增成功!');
} catch (error) {
alert('新增失敗:' + error.message);
}
}
<!-- 在 ResultsList 的 #card slot中 -->
<div class="customer-card">
<!-- 現有內容 -->
<div class="card-actions">
<BaseButton size="small" variant="secondary" @click="editCustomer(item)">
編輯
</BaseButton>
</div>
</div>
function editCustomer(customer) {
editingCustomer.value = { ...customer }; // 複製資料
showAddForm.value = true; // 顯示表單
}
async function handleFormSubmit(formData) {
try {
if (editingCustomer.value) {
// 編輯模式:更新現有客戶
const index = customers.findIndex((c) => c.id === editingCustomer.value.id);
if (index !== -1) {
customers[index] = { ...customers[index], ...formData };
}
} else {
// 新增模式:加入新客戶
const newCustomer = {
id: `C${Date.now()}`,
...formData,
createdAt: new Date().toISOString().split('T')[0],
};
customers.push(newCustomer);
}
showAddForm.value = false;
editingCustomer.value = null;
alert('儲存成功!');
} catch (error) {
alert('儲存失敗:' + error.message);
}
}
async function handleDeleteCustomer(id) {
if (!confirm('確定刪除?')) return;
// await customerStore.deleteCustomer(id)
// await customerStore.fetchCustomers()
}
本篇把 CustomerManagement 的 CRUD 動線補全至可運行骨架。當 API 就緒時,只需替換資料來源與錯誤處理。
明日,Day 26:[Systemの呼吸・參之型] 訂單流程 - 狀態機與流程設計。心を燃やせ 🔥!